package com.piece.module.file.util.pdf;

import com.itextpdf.forms.PdfAcroForm;
import com.itextpdf.forms.fields.PdfFormField;
import com.itextpdf.html2pdf.ConverterProperties;
import com.itextpdf.html2pdf.HtmlConverter;
import com.itextpdf.io.font.PdfEncodings;
import com.itextpdf.io.image.ImageDataFactory;
import com.itextpdf.io.util.StreamUtil;
import com.itextpdf.kernel.events.PdfDocumentEvent;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.*;
import com.itextpdf.kernel.pdf.canvas.parser.PdfCanvasProcessor;
import com.itextpdf.kernel.pdf.canvas.parser.listener.IPdfTextLocation;
import com.itextpdf.kernel.pdf.canvas.parser.listener.RegexBasedLocationExtractionStrategy;
import com.itextpdf.layout.Canvas;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.Image;
import com.itextpdf.layout.font.FontProvider;
import com.itextpdf.layout.font.FontSet;
import com.piece.core.framework.exception.ServerException;
import com.piece.core.framework.util.collection.CollectionUtil;
import com.piece.core.framework.util.string.QrcodeUtil;
import com.piece.core.framework.util.string.StringUtil;
import com.piece.core.framework.util.validator.ValidatorUtil;
import com.piece.module.file.util.convert.ChineseSplitCharacters;
import com.piece.module.file.util.pdf.event.FooterIEvent;
import com.piece.module.file.util.pdf.event.HeaderIEvent;
import com.piece.module.file.util.pdf.event.WaterMarIEvent;
import fr.opensagres.poi.xwpf.converter.pdf.PdfConverter;
import fr.opensagres.poi.xwpf.converter.pdf.PdfOptions;
import org.apache.commons.io.FileUtils;
import org.apache.pdfbox.io.MemoryUsageSetting;
import org.apache.pdfbox.multipdf.PDFMergerUtility;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.*;

public class PdfUtil {

    private static String fontPath = "";

    static {
        try {
            // 获取项目路径
            Properties props = System.getProperties();
            String rootPath = props.getProperty("user.dir");
            InputStream inputStream = PdfUtil.class.getClassLoader().getResourceAsStream("font/simsun.ttc");
            File upload = new File(rootPath, "upload/font");
            if (!upload.exists()) {
                upload.mkdirs();
            }
            upload = new File(rootPath, "upload/font/simsun.ttc");
            if (!upload.exists()) {
                upload.createNewFile();
                FileUtils.writeByteArrayToFile(upload, StreamUtil.inputStreamToArray(inputStream));
            }
            fontPath = upload.getPath();
        } catch (Exception e) {
        }
    }

    public static int pageNumber(InputStream is) throws Exception {
        PDDocument reader = null;
        try {
            reader = PDDocument.load(is);
            return reader.getNumberOfPages();
        } catch (Exception e) {
            throw new ServerException(e.getMessage());
        } finally {
            reader.close();
        }
    }

    /**
     * Html文本转Pdf文件流
     *
     * @param content html文本
     * @param os      输出流
     */
    public static void generateByHtml(String content, OutputStream os, PageSize pageSize) throws Exception {
        generateByHtml(content, null, os, pageSize);
    }

    /**
     * Html文本转Pdf文件流
     *
     * @param content html文本
     * @param os      输出流
     */
    public static void generateByHtml(String content, BuilderContext context, OutputStream os, PageSize pageSize) throws Exception {
        PdfWriter writer = new PdfWriter(os);
        PdfDocument pdf = new PdfDocument(writer);

        if (null == pageSize) {
            pageSize = PageSize.A4;
        }

        pdf.setDefaultPageSize(pageSize);
        pdf.getDefaultPageSize().applyMargins(0, 0, 0, 0, true);

        // 字体设置，解决中文不显示问题
        FontSet fontSet = new FontSet();
        //fontSet.addFont(fontPath + ",0", PdfEncodings.UTF8);
        fontSet.addFont("STSong-Light","UniGB-UCS2-H");
        ConverterProperties proper = new ConverterProperties();
        proper.setFontProvider(new FontProvider(fontSet));
        proper.setCharset(PdfEncodings.UTF8);

        Document document = HtmlConverter.convertToDocument(content, pdf, proper);
        document.setMargins(0, 0, 0, 0);
        //PdfFont font = PdfFontFactory.createFont(fontPath + ",0", PdfEncodings.UTF8);
        PdfFont font = PdfFontFactory.createFont("STSong-Light","UniGB-UCS2-H", PdfFontFactory.EmbeddingStrategy.FORCE_NOT_EMBEDDED);
        document.setFont(font);
        document.setFontSize(10f);
        document.setCharacterSpacing(0.1f);
        // 解决中文标点行首问题
        ChineseSplitCharacters chineseSplitCharacters = new ChineseSplitCharacters();
        document.setSplitCharacters(chineseSplitCharacters);

        if (null != context) {
            if (context.isShowHeader()) {
                //PdfFont headerFont = PdfFontFactory.createFont(fontPath + ",0", PdfEncodings.UTF8);
                PdfFont headerFont = PdfFontFactory.createFont("STSong-Light","UniGB-UCS2-H", PdfFontFactory.EmbeddingStrategy.FORCE_NOT_EMBEDDED);
                HeaderIEvent headerEvent = new HeaderIEvent(headerFont, context);
                pdf.addEventHandler(PdfDocumentEvent.START_PAGE, headerEvent);
            }
            if (context.isShowFooter()) {
                FooterIEvent footerEvent = new FooterIEvent(context, document, 0);
                pdf.addEventHandler(PdfDocumentEvent.START_PAGE, footerEvent);
            }
            if (StringUtil.isNotEmpty(context.getWaterMark()) || context.getBackground() != null) {
                //PdfFont waterFont = PdfFontFactory.createFont(fontPath + ",0", PdfEncodings.UTF8);
                PdfFont waterFont = PdfFontFactory.createFont("STSong-Light","UniGB-UCS2-H", PdfFontFactory.EmbeddingStrategy.FORCE_NOT_EMBEDDED);
                WaterMarIEvent waterMarEvent = new WaterMarIEvent(waterFont, context);
                pdf.addEventHandler(PdfDocumentEvent.END_PAGE, waterMarEvent);
            }
        }

        document.getRenderer().close();
        document.close();
        pdf.close();
    }

    /**
     * paf模板转Pdf文件流
     *
     * @param is      paf模板
     * @param data    参数
     * @param os      输出流
     */
    public static void generateByTemplateStream(InputStream is, Map<String, String> data, OutputStream os) throws Exception {
        PdfReader pdfReader = new PdfReader(is);
        PdfWriter writer = new PdfWriter(os);
        PdfDocument pdfDocument = new PdfDocument(pdfReader, writer);

        // 字体设置，解决中文不显示问题
        //PdfFont font = PdfFontFactory.createFont(fontPath + ",0", PdfEncodings.UTF8);
        PdfFont font = PdfFontFactory.createFont("STSong-Light","UniGB-UCS2-H", PdfFontFactory.EmbeddingStrategy.FORCE_NOT_EMBEDDED);

        PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDocument, false);
        Map<String, PdfFormField> fields = form.getFormFields();
        if (null != fields) {
            fields.entrySet().forEach(entry -> {
                String key = entry.getKey();
                PdfFormField field = entry.getValue();
                String value = data.get(key);
                if (ValidatorUtil.isChinese(value)) {
                    field.setFont(font);
                }
                if (StringUtil.isNotEmpty(value)) {
                    field.setValue(value);
                }
            });
        }

        form.flattenFields();
        pdfDocument.close();
    }

    /**
     * Pdf文件添加印章
     *
     * @param inputStream
     * @param context
     * @param os
     * @throws Exception
     */
    public static void stamper(InputStream inputStream, BuilderContext context, OutputStream os) throws Exception {
        PdfReader pdfReader = new PdfReader(inputStream);
        PdfWriter writer = new PdfWriter(os);
        PdfDocument pdfDocument = new PdfDocument(pdfReader, writer);
        int stamperPage = context.getStamperPage();
        byte[] stamper = context.getStamper();
        if (null != stamper) {
            for (int i = 1; i <= pdfDocument.getNumberOfPages(); i++) {
                if (stamperPage == -1 || stamperPage == i || (stamperPage == 0 && i == pdfDocument.getNumberOfPages())) {
                    PdfPage page = pdfDocument.getPage(i);
                    List<Rectangle> rectangles = dealWithKeyWord(page, context.getStamperKey());
                    if (CollectionUtil.isNotEmpty(rectangles)) {
                        rectangles.stream().forEach(rectangle -> {
                            Image stamperImage = new Image(ImageDataFactory.create(context.getStamper()));
                            stamperImage.setOpacity(0.8F);
                            Rectangle imageRectangle = new Rectangle(rectangle.getX() + context.getStamperOffsetX(), rectangle.getY() + context.getStamperOffsetY(),
                                    context.getStamperSizeX(), context.getStamperSizeY());
                            Canvas canvas = new Canvas(page, imageRectangle);
                            canvas.add(stamperImage).flush();
                            canvas.close();
                        });
                    }
                }
            }
        }
        pdfDocument.close();
    }

    /**
     * Pdf文件添加二维码
     *
     * @param inputStream
     * @param context
     * @param os
     * @throws Exception
     */
    public static void tdc(InputStream inputStream, BuilderContext context, OutputStream os) throws Exception {
        PdfReader pdfReader = new PdfReader(inputStream);
        PdfWriter writer = new PdfWriter(os);
        PdfDocument pdfDocument = new PdfDocument(pdfReader, writer);
        int tdcPage = context.getTdcPage();
        String tdcMark = context.getTdcMark();
        if (StringUtil.isNotEmpty(tdcMark)) {
            byte[] bytes = QrcodeUtil.encode(tdcMark, context.getTdcSizeX(), context.getTdcSizeY());
            for (int i = 1; i <= pdfDocument.getNumberOfPages(); i++) {
                if (tdcPage == -1 || tdcPage == i || (tdcPage == 0 && i == pdfDocument.getNumberOfPages())) {
                    PdfPage page = pdfDocument.getPage(i);
                    List<Rectangle> rectangles = dealWithKeyWord(page, context.getTdcMark());
                    if (CollectionUtil.isNotEmpty(rectangles)) {
                        rectangles.stream().forEach(rectangle -> {
                            Image stamperImage = new Image(ImageDataFactory.create(context.getStamper()));
                            stamperImage.setOpacity(0.8F);
                            Rectangle imageRectangle = new Rectangle(rectangle.getX() + context.getStamperOffsetX(), rectangle.getY() + context.getStamperOffsetY(),
                                    context.getStamperSizeX(), context.getStamperSizeY());
                            Canvas canvas = new Canvas(page, imageRectangle);
                            canvas.add(stamperImage).flush();
                            canvas.close();
                        });
                    }
                }
            }
        }
        pdfDocument.close();
    }

    private static List<Rectangle> dealWithKeyWord(PdfPage page, String key) {
        List<Rectangle> rectangles= new ArrayList<>();
        RegexBasedLocationExtractionStrategy strategy = new RegexBasedLocationExtractionStrategy(key);
        PdfCanvasProcessor canvasProcessor = new PdfCanvasProcessor(strategy);
        canvasProcessor.processPageContent(page);
        // 获取处理结果
        Collection<IPdfTextLocation> resultantLocations = strategy.getResultantLocations();
        // 自定义结果处理
        if (!resultantLocations.isEmpty()) {
            int index = resultantLocations.size();
            for (IPdfTextLocation item : resultantLocations) {
                if (--index == 0) {
                    rectangles.add(item.getRectangle());
                }
            }
        }

        return rectangles;
    }

    public static void docxToPdf(InputStream is, OutputStream os) {
        try {
            XWPFDocument xwpfDocument = new XWPFDocument(is);
            PdfConverter.getInstance().convert(xwpfDocument, os, PdfOptions.create());
        } catch (IOException e) {
            throw new ServerException(e.getMessage());
        } finally {
            try {
                is.close();
                os.close();
            } catch (Exception e) {}
        }
    }

    /**
     * pdf文档合并
     */
    public static void merge(List<File> files, OutputStream os, boolean delete) throws Exception {
        try {
            PDFMergerUtility mergerUtility = new PDFMergerUtility();
            for (File file : files) {
                mergerUtility.addSource(file);
            }

            // 设置合并生成pdf文件名称
            mergerUtility.setDestinationStream(os);
            // 合并pdf
            mergerUtility.mergeDocuments(MemoryUsageSetting.setupMainMemoryOnly());
        } catch (Exception e) {
            throw new ServerException(e.getMessage());
        } finally {
            if (delete) {
                for (File file : files) {
                    file.delete();
                }
            }
        }
    }
}
